package org.meb.spdwldr;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.ManagedType;

import org.apache.commons.lang.time.DateUtils;
import org.meb.spdwldr.core.SortOrder.Direction;

public class EventSourceDao {

	private EntityManager createEntityManager() {
		return EMF.get().createEntityManager();
	}

	public List<EventSource> find(EventSourceQuery query) {
		EntityManager em = createEntityManager();
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<EventSource> cq = cb.createQuery(EventSource.class);
		Root<EventSource> evsr = cq.from(EventSource.class);
		List<Predicate> predicates = new ArrayList<Predicate>();
		if (query.getUpdateDateFrom() != null) {
			predicates.add(cb.greaterThanOrEqualTo(evsr.get(EventSource_.updateDate),
					query.getUpdateDateFrom()));
		}
		if (query.getUpdateDateTo() != null) {
			predicates.add(cb.lessThanOrEqualTo(evsr.get(EventSource_.updateDate),
					query.getUpdateDateTo()));
		}
		if (predicates.size() > 0) {
			cq.where(predicates.toArray(new Predicate[predicates.size()]));
		}

		if (!query.getSortOrder().isEmpty()) {
			ManagedType<EventSource> mt = em.getMetamodel().managedType(EventSource.class);
			Iterator<Entry<String, Direction>> iter = query.getSortOrder().iterator();
			ArrayList<Order> order = new ArrayList<Order>();
			while (iter.hasNext()) {
				Entry<String, Direction> entry = iter.next();
				Path<?> path = evsr.get(mt.getSingularAttribute(entry.getKey()));
				if (entry.getValue() == Direction.ASC) {
					order.add(cb.asc(path));
				} else if (entry.getValue() == Direction.DESC) {
					order.add(cb.desc(path));
				}
			}
			cq.orderBy(order);
		}

		List<EventSource> eventSources = em.createQuery(cq).getResultList();
		for (EventSource source : eventSources) {
			em.detach(source);
		}
		return eventSources;
	}

	public EventSource find(String uri) {
		EntityManager em = createEntityManager();
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<EventSource> cq = cb.createQuery(EventSource.class);
		cq.where(cb.equal(cq.from(EventSource.class).get(EventSource_.eventUri), uri));
		List<EventSource> list = em.createQuery(cq).getResultList();
		if (list != null) {
			if (list.size() == 0) {
				return null;
			} else if (list.size() == 1) {
				EventSource source = list.get(0);
				em.detach(source);
				return source;
			} else {
				throw new RuntimeException("One result was expected, got " + list.size());
			}
		}
		return null;
	}

	public void persist(EventSource source) {
		corrrectDateAndTime(source);
		createEntityManager().persist(source);
	}

	public void merge(EventSource source) {
		corrrectDateAndTime(source);
		createEntityManager().merge(source);
	}

	public void delete(Long id) {
		createEntityManager().remove(id);
	}

	private void corrrectDateAndTime(EventSource source) {
		Date operDate = DateUtils.truncate(new Date(), Calendar.SECOND);
		if (source.getInsertDate() == null) {
			source.setInsertDate(operDate);
		}
		source.setUpdateDate(operDate);

		Date eventDate = source.getEventDate();
		if (eventDate != null) {
			source.setEventDate(DateUtils.truncate(eventDate, Calendar.DAY_OF_MONTH));
			if (source.getEventTime() == null) {
				source.setEventTime(eventDate);
			}
		}
	}

}
